﻿//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="DylanBox">
// Copyright (c) DylanBox Corporation. All rights reserved.
// </copyright>
// <summary>
//      Using result.AsyncWaitHandle.WaitOne(2000); not blocking
//      Using result.AsyncWaitHandle.WaitOne(); blocking
//  
//  Result:
//      Before making the Async call... Thread ID = 1
//      After making the Async call... Thread ID = 1
//      Perform more work as the other thread works...
//      The slow method... executes...on thread 3
//      10...
//      9...
//      8...
//      7...
//      6...
//      5...
//      The async task is going on thread # 3
//      4...
//      3...
//      2...
//      Before calling WaitOne... 0 milliseconds
//      Well, I know waiting could be boring, but at the moment I am still waiting...
//      The async task is going on thread # 3
//      Argghh... when will this end??
//      The async task is going on thread # 3
//      The async task is going on thread # 3
//      The async task is going on thread # 3
//      After calling WaitOne... 21005 milliseconds
//      The call got executed on thread 3
//      The value returned was - I worked in my sleep for 30 seconds
// </summary>
// <reference>
//    http://www.dotnetscraps.com/dotnetscraps/post/Explaining-Delegates-in-C-Part-5-(Asynchronous-Callback-Way-2).aspx
// </reference>
// <author>Dylan</author>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace EventAndDelegateDemo
{
    //The delegate must have the same signature as the method. In this case,
    //we will make it same as TortoiseMethod
    public delegate string TortoiseCaller(int seconds, out int threadId);

    public class TortoiseClass
    {
        // The method to be executed asynchronously.
        public string TortoiseMethod(int seconds, out int threadId)
        {
            Console.WriteLine("The slow method... executes...on thread {0}", Thread.CurrentThread.ManagedThreadId);
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(seconds / 5 * 1000);
                Console.WriteLine("The async task is going on thread # {0}", Thread.CurrentThread.ManagedThreadId);
            }
            threadId = Thread.CurrentThread.ManagedThreadId;
            return String.Format("I worked in my sleep for {0} seconds", seconds.ToString());
        }
    }

    //Now, that we are done with the declaration part, let's proceed to
    //consume the classes and see it in action
    //The algorithm would be very simple...
    //         1. Call delegate's BeginInvoke
    //         2. Do some work on the main thread
    //         3. Call the result's AsyncWaitHandle.WaitOne() which would be a blocking call
    //         4. Call EndInvoke which won't be a blocking call this time!
    //         5. Close the result's AsyncWaitHandle, explicitly.
    public class TortoiseConsumer
    {
        static void Main()
        {
            //Instantiate a new TortoiseClass
            TortoiseClass tc = new TortoiseClass();
            //Let's create the delegate now
            TortoiseCaller caller = new TortoiseCaller(tc.TortoiseMethod);
            //The asynchronous method puts the thread id here
            int threadId;
            //Make the async call. Notice that this thread continues to run after making this call
            Console.WriteLine("Before making the Async call... Thread ID = {0}", Thread.CurrentThread.ManagedThreadId);
            IAsyncResult result = caller.BeginInvoke(30, out threadId, null, null);
            //After calling the method asynchronously, the main thread continues to work...
            Console.WriteLine("After making the Async call... Thread ID = {0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Perform more work as the other thread works...");
            for (int i = 10; i > 1; i--)
            {
                Thread.Sleep(1000);
                Console.WriteLine("{0}...", i);
            }
            Stopwatch s = new Stopwatch();
            //Calling WaitOne is a blocking call. As soon as you call WaitOne, you won't proceed further
            //in this main thread until the Async call completes
            Console.WriteLine("Before calling WaitOne... {0} milliseconds", s.ElapsedMilliseconds.ToString());
            s.Start();
            //The next call can be a blocking call (in our case it WILL be a blocking call since the Tortoise
            //method takes 30 seconds to complete. By now, already 12 seconds are over!
            result.AsyncWaitHandle.WaitOne(2000);
            //The good thing is that, now you can do update the client while still waiting for the call to complete
            Console.WriteLine("Well, I know waiting could be boring, but at the moment I am still waiting...");
            //Waiting for 5 seconds now!
            result.AsyncWaitHandle.WaitOne(5000);
            //Updating once again...
            Console.WriteLine("Argghh... when will this end??");
            //Waiting till the async call is complete (Notice that this can be blocking!!)
            result.AsyncWaitHandle.WaitOne();
            s.Stop();
            Console.WriteLine("After calling WaitOne... {0} milliseconds", s.ElapsedMilliseconds.ToString());
            //Notice that this call will NOT be a blocking call as it was in our previous example!
            string returnValue = caller.EndInvoke(out threadId, result);
            //Close the wait handle. This is important, since it is not automatically cleared.
            //Only the next GC can collect this native handle. So, it is a good practise to clear
            //this handle as soon as you are done with it.
            result.AsyncWaitHandle.Close();
            Console.WriteLine("The call got executed on thread {0}", threadId);
            Console.WriteLine("The value returned was - {0}", returnValue);

            Console.Read();
        }
    }
}
